from fastmcp import FastMCP
from typing import Annotated, List, Dict, Any, Optional
from pydantic import Field, BaseModel
from starlette.concurrency import run_in_threadpool # 用于在异步函数中运行同步代码
import json
import base64 # 用于将PDF二进制数据编码为字符串
import os
# 导入数据库操作工具
import utils.db_helper as db_helper
import utils.pdf as pdf_helper
import utils.upload as upload_helper



class ProductOrderItem(BaseModel):
    product_id: str
    quantity: int

class CreateOrderRequest(BaseModel):
    purchaser: str
    products_in_order: List[ProductOrderItem]
    expected_delivery_time: Optional[str] = Field( # 新增：期望到貨時間
        None,
        description="期望的到貨時間，格式為文本，例如：'2025-06-10 下午' 或 '明天上午'。此為可選字段。"
    )
    
class SignOrderRequest(BaseModel):
    order_id: str

class QueryOrdersParams(BaseModel):
    purchaser: str
    order_time_min: Optional[str] = Field(None, description="下单时间范围最小值，格式：YYYY-MM-DD HH:MM:SS")
    order_time_max: Optional[str] = Field(None, description="下单时间范围最大值，格式：YYYY-MM-DD HH:MM:SS")
    is_signed: Optional[bool] = Field(None, description="是否已签收 (True: 已签收, False: 未签收)。如果为None，则不作为过滤条件。")
    is_settled: Optional[bool] = Field(None, description="是否已结算 (True: 已结算, False: 未结算)。如果为None，则不作为过滤条件。")


mcpaiDemo = FastMCP(
    name="mcp-aiDemo",
    description="为基于AI Agent开发的采购助手，提供MCP服务的工具支持",
)


@mcpaiDemo.tool(tags={"read"})
async def query_stock(
    keywords: Annotated[
        str,
        Field(
            description=(
                "查询条件关键字：可以是商品名称，例如：茅台酒。也可以是或商品类型，例如：白酒。 "
            )
        ),
    ]
    )-> str:
    """根据查询条件获取特定商品的库存信息。

    Args:
        keywords: 查询条件关键字

    Returns:
        库存商品信息,json格式
    """
    
  
    # 异步执行同步的数据库查询函数
    result = await run_in_threadpool(db_helper.query_stock, keywords)
    return json.dumps(result, ensure_ascii=False)

@mcpaiDemo.tool(tags={"write"})
async def create_order(
     order_request: Annotated[
        CreateOrderRequest,
        Field(
            description="為指定採購方創建新訂單，並更新商品庫存。支援一次創建多個商品項的訂單。"
        )
    ]
) -> str:
    """创建新的采购订单，并更新商品库存。

    Args:
        order_request: 包含訂單詳細資訊的請求體。
            - purchaser (str): 採購方公司名稱。
            - products_in_order (List[ProductOrderItem]): 訂單商品列表。
            - expected_delivery_time (str, optional): 期望的到貨時間。

    Returns:
        订单创建结果，包含订单ID和总金额，json格式。
        如果库存不足或商品不存在，将返回错误信息。
    """
    # 将 Pydantic 模型的列表转换为 db_helper.create_order 函数期望的 dict 列表
    products_list_for_db = [item.model_dump() for item in order_request.products_in_order]
    
    result = await run_in_threadpool(
        db_helper.create_order,
        order_request.purchaser,
        products_list_for_db,
        order_request.expected_delivery_time # 傳遞新的參數
    )
    return json.dumps(result, ensure_ascii=False)


@mcpaiDemo.tool(tags={"read", "file_generation"})
async def generate_settlement_statement(
    purchaser_name: Annotated[
        str,
        Field(
            description="需要生成结算单的采购方公司名称，例如：北京四季酒店。"
        )
    ]
) -> Dict[str, Any]:
    """为指定采购方生成一份包含所有未结算订单明细的结算单，并以Base64编码的PDF形式返回。

    Args:
        purchaser_name: 采购方公司名称。

    Returns:
        json格式的字典，包含生成的PDF文件信息。
    """
   
    
    try:
         # 异步执行同步的PDF生成函数
        filename = await run_in_threadpool(pdf_helper.generate_settlement_pdf, purchaser_name)
        
        '''
        # 获取文件的绝对路径
        absolute_path = os.path.abspath(filename)
        print(f"生成的结算单PDF文件路径: {absolute_path}")
        
        dify_response  = await run_in_threadpool(upload_helper.upload_file_to_dify, file_path=absolute_path)
        print(f"文件上传到Dify的响应: {dify_response}")
        upload_file_id = dify_response.get('id')
        
        if not upload_file_id:
            raise Exception("Dify上傳響應中未包含文件ID。")
        print(f"文件成功上傳到Dify，文件ID: {upload_file_id}")
        '''
        # 4. 返回 Dify Agent 要求的格式
        return {
            "file_name": os.path.basename(filename),
            "file_url": f"pdf://{os.path.basename(filename)}",  
            "message": "结算单生成并上传成功。",
            "status": "success"
        }
    except Exception as e:
        print(f"生成结算单或上传文件时发生错误: {e}")
        return {
            "error": str(e)
        }


@mcpaiDemo.tool(tags={"write", "logistics"}) # 增加 tags
async def sign_order( # 更名为 sign_order 更清晰
    sign_request: Annotated[
        SignOrderRequest,
        Field(
            description="要进行签收操作的订单的ID，例如：20250605001。"
        )
    ]
) -> str:
    """确认订单商品已签收（完成物流环节），仅记录签收时间，不涉及财务结算。

    Args:
        sign_request: 包含要签收的订单ID的请求体。

    Returns:
        订单签收结果，json格式。如果订单不存在或已签收，将返回相应信息。
    """
    result = await run_in_threadpool(db_helper.sign_order, sign_request.order_id)
    return json.dumps(result, ensure_ascii=False)

@mcpaiDemo.tool(tags={"write", "finance"}) # 增加 tags
async def settle_order( # 新增结算工具
    settle_request: Annotated[
        SignOrderRequest, # 复用 SignOrderRequest 模型，或者创建一个新的 SettleOrderRequest
        Field(
            description="要进行结算操作的订单的ID，例如：20250605001。"
        )
    ]
) -> str:
    """对已签收且未结算的订单进行财务结算，将其标记为已结算。

    Args:
        settle_request: 包含要结算的订单ID的请求体。

    Returns:
        订单结算结果，json格式。如果订单未签收、已结算或不存在，将返回相应信息。
    """
    result = await run_in_threadpool(db_helper.settle_order, settle_request.order_id)
    return json.dumps(result, ensure_ascii=False)

@mcpaiDemo.tool(tags={"read", "order_management"})
async def query_orders(
    params: Annotated[
        QueryOrdersParams,
        Field(
            description="查询订单的条件，包含采购方公司名称（必选）以及可选的下单时间范围、签收状态和结算状态。"
        )
    ]
) -> str:
    """根据多种条件查询订单的详细信息，包括订单明细和关联商品的完整信息。

    Args:
        params: 包含查询条件的Pydantic模型。
            - purchaser (str): 采购方公司名称 (必选)。
            - order_time_min (str, optional): 下单时间范围最小值 (YYYY-MM-DD HH:MM:SS)。
            - order_time_max (str, optional): 下单时间范围最大值 (YYYY-MM-DD HH:MM:SS)。
            - is_signed (bool, optional): 是否已签收 (True: 已签收, False: 未签收)。
            - is_settled (bool, optional): 是否已结算 (True: 已结算, False: 未结算)。

    Returns:
        JSON格式的字符串，包含符合条件的订单列表。每个订单包含其完整的订单信息、订单明细以及每个明细关联的商品完整信息。
        如果查询出错，返回包含 'error' 字段的JSON字符串。
    """
    # 异步执行同步的数据库查询函数
    result = await run_in_threadpool(
        db_helper.query_orders,
        purchaser=params.purchaser,
        order_time_min=params.order_time_min,
        order_time_max=params.order_time_max,
        is_signed=params.is_signed,
        is_settled=params.is_settled
    )
    
    # 确保返回的是JSON字符串，即使是错误信息
    return json.dumps(result, ensure_ascii=False)

@mcpaiDemo.tool(tags={"read"})
async def get_img(
    file_name: Annotated[
        str,
        Field(
            description="需要获取的图片文件名"
        )
    ]
) -> Dict[str, Any]:
    
    """根据用户需要的图片文件名，返回二进制流的图片。

    Args:
        file_name: 文件名。

    Returns:
        json格式的回复，包含二进制流的图片内容以及文本消息。
    """
    
    with open("imgs/output_bill.png", "rb") as image_file:
        image_binary_data = image_file.read()
        # Base64 encode the binary data
        encoded_image = base64.b64encode(image_binary_data).decode('utf-8')
        
    return [  
        {  
            "type": "blob",  
            "message": {"blob": encoded_image},  # Send the base64 encoded string
            "meta": {  
                "mime_type": "image/jpeg",  
                "filename": "generated_image.jpg"  
            }  
        },  
        {  
            "type": "text",  
            "message": {"text": "这是生成的图片文件，请查看。"},  
        }  
    ]



if __name__ == "__main__":
    # 运行 FastMCP 服务
    mcpaiDemo.run(
        transport="sse", # 默认的传输方式，通常用于Agent
        host="0.0.0.0",
        port=8000,
    )